Skip to content

fix(staged): keep git subprocesses from wedging the user's terminal#757

Merged
matt2e merged 2 commits into
mainfrom
terminal-still-dying
May 29, 2026
Merged

fix(staged): keep git subprocesses from wedging the user's terminal#757
matt2e merged 2 commits into
mainfrom
terminal-still-dying

Conversation

@matt2e
Copy link
Copy Markdown
Contributor

@matt2e matt2e commented May 29, 2026

Summary

Git subprocesses spawned by staged could wedge the outer terminal when ssh fell back to prompting for credentials. ssh would open(\"/dev/tty\") directly — bypassing our piped stdio — and tcsetpgrp the user's TTY onto its own process group. When that group died, the outer zsh got stuck with EIO.

This change applies two layers of defense to every git invocation in git/cli.rs:

  • force_non_interactive sets GIT_TERMINAL_PROMPT=0 and a default GIT_SSH_COMMAND with BatchMode=yes and ConnectTimeout=10 so ssh fails fast with Permission denied instead of prompting. A user-provided GIT_SSH_COMMAND from the captured shell env (e.g. a corporate ssh wrapper) is preserved.
  • detach_from_ctty calls setsid() in pre_exec so the child has no controlling TTY to grab, even if a future refactor reattaches stdio.

Includes unit tests covering both the default-injection and user-respects paths for GIT_SSH_COMMAND.

Test plan

  • cargo test in apps/staged/src-tauri (covered by CI: crates-test)
  • crates-fmt / crates-lint / staged-ci / differ-ci all green on push

matt2e added 2 commits May 29, 2026 11:29
Centralize TTY hardening in `run_with_env` so every git invocation gets
both layers: `GIT_TERMINAL_PROMPT=0` + `GIT_SSH_COMMAND=ssh -o
BatchMode=yes -o ConnectTimeout=10` make ssh refuse to prompt at the
semantic level, and `setsid()` via `pre_exec` ensures ssh can't
`open("/dev/tty")` to bypass piped stdio. The previous fix (#756) only
covered the env-capture shells; background_sync's `git fetch` still
inherited the user's ctty and could surface a passphrase prompt that
left the outer zsh wedged on EIO.

Signed-off-by: Matt Toohey <contact@matttoohey.com>
force_non_interactive unconditionally clobbered any GIT_SSH_COMMAND
carried in by the captured shell snapshot — silently overriding
corporate ssh wrappers that pin identity files, route through a jump
host, or talk to an SSH CA helper. Skip the default injection when the
captured env already defines GIT_SSH_COMMAND; trust those users to keep
their wrapper non-interactive and lean on detach_from_ctty (the
structural half of the pair) to keep an errant prompt from stealing the
outer TTY.

Signed-off-by: Matt Toohey <contact@matttoohey.com>
@matt2e matt2e requested review from baxen and wesbillman as code owners May 29, 2026 02:04
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 449355efc5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +213 to +216
command.env(
"GIT_SSH_COMMAND",
"ssh -o BatchMode=yes -o ConnectTimeout=10",
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve configured SSH commands

For repos that rely on core.sshCommand (for example to select a deploy key, wrapper, or non-default SSH binary), injecting GIT_SSH_COMMAND here makes Git ignore that repo/user config; I checked the Git docs for core.sshCommand, which state it is overridden when GIT_SSH_COMMAND is set. This affects the existing fetch/push paths that call cli::run, so those repos can start failing authentication even though they worked before unless the user also exports GIT_SSH_COMMAND.

Useful? React with 👍 / 👎.

@matt2e matt2e merged commit da496af into main May 29, 2026
5 checks passed
@matt2e matt2e deleted the terminal-still-dying branch May 29, 2026 02:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant